﻿// (c) Norbert Huffschmid
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SpinDotters.Core
{
    /// <summary>
    /// Toggle switch control
    /// </summary>
    [TemplatePart(Name = "PART_ToggleUpDown", Type = typeof(DomainUpDown))]
    public class ToggleSwitch : CheckBox
    {
        private const int CLICK_DETECTION_TIME = 500; // msec

        private const string TEXTON_DEFAULT = "On";
        private const string TEXTOFF_DEFAULT = "Off";
        private static Brush BRUSH_ON_DEFAULT = new SolidColorBrush(Colors.Transparent);
        private static Brush BRUSH_OFF_DEFAULT = new SolidColorBrush(Colors.Transparent);

        private DomainUpDown toggleUpDown;
        private DateTime clickTimeStamp;


        /// <summary>
        /// Identifies the On text dependency property.
        /// </summary>
        public static readonly DependencyProperty TextOnProperty =
            DependencyProperty.Register(
                "TextOn",
                typeof(string),
                typeof(ToggleSwitch),
                new PropertyMetadata(TEXTON_DEFAULT, null)
            );

        /// <summary>
        /// Gets or set the On text of the toggle switch.
        /// </summary>
        [Category("SpinDotters")]
        public string TextOn
        {
            get { return (string)GetValue(TextOnProperty); }
            set { SetValue(TextOnProperty, value); }
        }

        /// <summary>
        /// Identifies the Off text dependency property.
        /// </summary>
        public static readonly DependencyProperty TextOffProperty =
            DependencyProperty.Register(
                "TextOff",
                typeof(string),
                typeof(ToggleSwitch),
                new PropertyMetadata(TEXTOFF_DEFAULT, null)
            );

        /// <summary>
        /// Gets or set the Off text of the toggle switch.
        /// </summary>
        [Category("SpinDotters")]
        public string TextOff
        {
            get { return (string)GetValue(TextOffProperty); }
            set { SetValue(TextOffProperty, value); }
        }

        /// <summary>
        /// Identifies the On brush dependency property.
        /// </summary>
        public static readonly DependencyProperty BrushOnProperty =
            DependencyProperty.Register(
                "BrushOn",
                typeof(Brush),
                typeof(ToggleSwitch),
                new PropertyMetadata(BRUSH_ON_DEFAULT, null)
            );

        /// <summary>
        /// Gets or set the On brush of the toggle switch.
        /// </summary>
        [Category("SpinDotters")]
        public Brush BrushOn
        {
            get { return (Brush)GetValue(BrushOnProperty); }
            set { SetValue(BrushOnProperty, value); }
        }

        /// <summary>
        /// Identifies the Off brush dependency property.
        /// </summary>
        public static readonly DependencyProperty BrushOffProperty =
            DependencyProperty.Register(
                "BrushOff",
                typeof(Brush),
                typeof(ToggleSwitch),
                new PropertyMetadata(BRUSH_OFF_DEFAULT, null)
            );

        /// <summary>
        /// Gets or set the Off brush of the toggle switch.
        /// </summary>
        [Category("SpinDotters")]
        public Brush BrushOff
        {
            get { return (Brush)GetValue(BrushOffProperty); }
            set { SetValue(BrushOffProperty, value); }
        }


        /// <summary>
        /// Constructor
        /// </summary>
        public ToggleSwitch()
        {
            this.DefaultStyleKey = typeof(ToggleSwitch);
            this.clickTimeStamp = DateTime.Now;
            this.IsThreeState = false;

            this.DataContext = this;
        }


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.toggleUpDown = this.GetTemplateChild("PART_ToggleUpDown") as DomainUpDown;

            if (this.toggleUpDown != null)
            {
                this.toggleUpDown.SelectedItemChanged += new EventHandler(toggleUpDown_SelectedItemChanged);
                this.toggleUpDown.MouseLeftButtonDown += new MouseButtonEventHandler(toggleUpDown_MouseLeftButtonDown);
                this.toggleUpDown.MouseLeftButtonUp += new MouseButtonEventHandler(toggleUpDown_MouseLeftButtonUp);
            }

            this.Checked += new RoutedEventHandler(ToggleSwitch_CheckedChanged);
            this.Unchecked += new RoutedEventHandler(ToggleSwitch_CheckedChanged);
            
            ToggleSwitch_CheckedChanged(this, new RoutedEventArgs());
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            // do not treat event as handled
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            // do not treat event as handled
        }


        private void toggleUpDown_SelectedItemChanged(object sender, EventArgs e)
        {
            this.IsChecked = (this.toggleUpDown.SelectedIndex == 0);
        }

        private void toggleUpDown_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.clickTimeStamp = DateTime.Now;
        }

        private void toggleUpDown_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            TimeSpan clickDelay = DateTime.Now - this.clickTimeStamp;

            if (clickDelay.TotalMilliseconds < CLICK_DETECTION_TIME)
            {
                // change state
                this.IsChecked = !this.IsChecked;
            }
        }

        private void ToggleSwitch_CheckedChanged(object sender, RoutedEventArgs e)
        {
            if (this.toggleUpDown != null)
            {
                if (this.IsChecked == true)
                {
                    this.toggleUpDown.SelectedIndex = 0;
                    this.toggleUpDown.Background = this.BrushOn;
                }
                else
                {
                    this.toggleUpDown.SelectedIndex = 1;
                    this.toggleUpDown.Background = this.BrushOff;
                }
            }
        }
    }
}
